package com.link.base.core.basic.config;

import com.link.core.cllog.LCLogger;
import com.link.core.util.SpringUtil;
import com.netflix.discovery.DiscoveryClient;
import org.apache.catalina.connector.Connector;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.ContextClosedEvent;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 自定义servlet容器工厂：在服务停止前，先从eureka中下线，然后睡眠十秒，最后停止服务。
 * 避免在重启服务前几秒钟内依然有请求路由到此服务
 * @author 郭闪
 * @date 2019/12/13
 */
//@Configuration
public class GracefulShutdownConfig {

    /**
     * 用于接受shutdown事件
     *
     * @return
     */
    @Bean
    public GracefulShutdown gracefulShutdown() {
        return new GracefulShutdown();
    }

    /**
     * 用于注入 connector
     *
     * @return
     */
    @Bean
    public ServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
        tomcat.addConnectorCustomizers(gracefulShutdown());
        return tomcat;

    }

    private static class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
        private volatile Connector connector;
        private final int waitTime = 180;

        @Override
        public void customize(Connector connector) {
            this.connector = connector;
        }

        /**
         * 应用关闭事件回调函数
         * @author 郭闪
         * @date 2019/12/26
         * @param event 应用关闭事件
         *
         */
        @Override
        public void onApplicationEvent(ContextClosedEvent event) {
            if (null != this.connector) {
                //获取服务发现类，并将服务从eureka下线
                DiscoveryClient discoveryClient = SpringUtil.getBean(DiscoveryClient.class);
                discoveryClient.shutdown();
                LCLogger.info().withMessageKey("startServer").withMessage("已将服务状态设为下线，开始睡十秒");
                try {
                    Thread.sleep(10000);
                    LCLogger.info().withMessageKey("startServer").withMessage("十秒已过，开始停止服务");
                } catch (InterruptedException e) {
                    LCLogger.withException(e);
                    Thread.currentThread().interrupt();
                }

                this.connector.pause();
                Executor executor = this.connector.getProtocolHandler().getExecutor();
                if (executor instanceof ThreadPoolExecutor) {
                    try {
                        ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
                        threadPoolExecutor.shutdown();
                        if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) {
                            LCLogger.error().withMessageKey("onApplicationEvent")
                                    .withMessage("Tomcat thread pool did not shut down gracefully within "
                                            + waitTime + " seconds. Proceeding with forceful shutdown");
                        }
                    } catch (InterruptedException ex) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }
    }
}
